/* api */
import { conversationChat, initChat } from '@src/api/LLMApi'
import { textToReports } from "@src/api/AIApi";
/* components */
import { 
  ChatAIInputBar, 
  ChatAIMessageList, 
  ChatAIHistoryMessageTip,
  ChatAIAppTag,
  ChatAIPromptTag
} from '@src/component/business/BizChatPanel/chat/components'
import { Loading } from 'element-ui';
/* enum */
import { ChatSourceDocumentTypeEnum, ComponentNameEnum } from '@src/component/business/BizChatPanel/chat/model/enum'
/* hooks */
import { useLoading } from '@hooks/useLoading'
import {
  useChatFetchContentSecurityText,
  useChatFetchConversation,
  useChatFetchDeleteMessage,
  useChatFetchHistoryMessage,
  useChatFetchQuestionInStandardLib,
  useChatFetchSendNewMessage
} from '@src/component/business/BizChatPanel/chat/hooks'
/* model */
import { 
  ChatIMMessageItemType,
  DEFAULT_CHAT_SYSTEM_MESSAGE, 
  OpenAIMessageItemType, 
  OpenAIMessageRoleEnum, 
  STREAM_BUSINESS_SOURCE_DOCUMENTS
} from '@src/component/business/BizChatPanel/chat/model'
import { 
  ChatBaseParams, 
  ChatConversationParams, 
  ChatInitParams, 
  GetChatConversationModel, 
  GetChatConversationShareModel, 
  GetChatHistoryMessageModel, 
  SendChatConversationContentModel, 
  SendChatConversationContentShareModel
} from '@model/param/in/AI'
/* vue */
import { computed, defineComponent, PropType, ref } from 'vue'
/* util */
import { 
  chatIMMessageToAIMessage,
  getAssistantLoadingMessage, 
  getAssistantStopMessage, 
  getSystemMessageByContent, 
  getUserMessageByContent,
  isAssistantMessage,
  isNotUserMessage,
  isUserMessage
} from '@src/component/business/BizChatPanel/chat/util'
import { cloneDeep, isAsyncFunction, isEmpty, isFalsy, isFunction, isNotEmpty, isNotEqual, isString, isUndefined } from '@src/util/type'
import { uuid } from '@src/util/lang/string'
import Platform from '@src/util/platform'
import { parse_with_default_value } from '@src/util/lang/object';
/* util */
import { getRootWindowInitData } from '@src/util/window'
/* types */
import { ChatBusinessSourceDocumentType } from '@src/component/business/BizChatPanel/chat/types'
import { ChatConversationResult } from '@model/param/out/AI'
import { SettingGPTPromptWordItem, SettingGPTServiceItem } from '@gpt/types'
import Page from '@model/Page'
import { SettingGPTServiceTypeEnum } from '@src/modules/setting/gpt/model';
import MsgModel from '@model/MsgModel';

type ChatAIInputBarComponent = InstanceType<typeof ChatAIInputBar>;
type ChatAIMessageListElement = InstanceType<typeof HTMLDivElement>;

export type ChatAIViewProps = {
  isShare: boolean
}

export interface ChatAIViewSetupState {
  
}

export enum ChatAIViewEventEnum {
  Input = 'input',
  AppTag = 'appTag',
}

export default defineComponent({
  name: ComponentNameEnum.ChatAIView,
  components: {
    [ChatAIInputBar.name]: ChatAIInputBar,
    [ChatAIMessageList.name]: ChatAIMessageList,
    [ChatAIHistoryMessageTip.name]: ChatAIHistoryMessageTip,
  },
  props: {
    userId: {
      type: String,
      default: ''
    },
    robotId: {
      type: String,
      default: ''
    },
    tenantId: {
      type: String,
      default: ''
    },
    isShare: {
      type: Boolean,
      default: false
    },
    stream: {
      type: Boolean,
      default: false
    },
    currentApp: {
      type: Object as PropType<SettingGPTServiceItem>,
      default: () => ({})
    },
    isDisabled: {
      type: Boolean,
      default: false
    },
    chatSystemMessage: {
      type: String,
      default: ''
    },
    onAppTag: {
      type: Function
    }
  },
  setup(props: ChatAIViewProps, { slots, emit }) {
    
    // 加载状态
    const { loading, showLoading, hideLoading } = useLoading()
    // 是否是外部分享的
    const _isShare = computed(() => props.isShare)
    
    // 发送新消息
    const { 
      fetchSendNewMessage,
      loading: fetchSendNewMessageLoading
    } = useChatFetchSendNewMessage(_isShare)
    
    // 删除消息
    const {
      fetchDeleteMessage,
      loading: fetchDeleteMessageLoading
    } = useChatFetchDeleteMessage()
    
    // 获取会话
    const {
      fetchChatConversation,
      conversationInfo,
      loading: fetchConversationLoading
    } = useChatFetchConversation(_isShare)
    
    // 获取历史消息
    const { 
      fetchHistoryMessage, 
      setMessageList,
      setMessagePage,
      messageList, 
      messagePage,
      loading: fetchHistoryMessageLoading
    } = useChatFetchHistoryMessage()
    
    // 内容安全检查
    const {
      fetchContentSecurityText,
      loading: fetchContentSecurityTextLoading
    } = useChatFetchContentSecurityText()
    
    // 查询问题是否在标准库中
    const {
      fetchQuestionInStandardLib,
      loading: fetchQuestionInStandardLibLoading,
      standardQuestion
    } = useChatFetchQuestionInStandardLib(_isShare)
    
    const page = ref({
      list: []
    })
    
    return {
      page,
      loading,
      messageList,
      messagePage,
      fetchConversationLoading,
      conversationInfo,
      fetchSendNewMessageLoading,
      fetchHistoryMessageLoading,
      fetchContentSecurityTextLoading,
      fetchQuestionInStandardLibLoading,
      standardQuestion,
      
      setMessageList,
      setMessagePage,
      showLoading,
      hideLoading,
      fetchDeleteMessage,
      fetchSendNewMessage,
      fetchHistoryMessage,
      fetchChatConversation,
      fetchContentSecurityText,
      fetchQuestionInStandardLib,
    }
    
  },
  data() {
    return {
      messages: [] as OpenAIMessageItemType[],
      focusNumber: 0,
      isLastAssistantMessageError: false,
      messageList: [] as OpenAIMessageItemType[],
      page: new Page({
        pageNum: 1
      }),
      isFirstClickHistoryMessageTip: true,
      chatLoading: false,
      foldHistoryMessageList: [] as OpenAIMessageItemType[],
      id: '',
      loadingInstance: null as any,
      controller: null as unknown as AbortController,
      isStreaming: false,
      isInitFail: false,
    }
  },
  computed: {
    attrs(): Record<string, any> {
      return {
        directives: [
          {
            name: 'loading',
            value: this.loading
          }
        ]
      }
    },
    /** 
     * @description 输入框组件
    */
    chatAIInputBarComponent(): ChatAIInputBarComponent {
      return this.$refs.ChatAIInputBarComponent as ChatAIInputBarComponent
    },
    /** 
     * @description 消息列表组件
    */
    chatAIMessageListElement(): ChatAIMessageListElement {
      return this.$refs.ChatAIMessageListElement as any
    },
    /** 
     * @description 会话 id
    */
    conversationId(): string {
      return this.conversationInfo?.id
    },
    /** 
     * @description 获取会话参数
    */
    conversationParams(): GetChatConversationShareModel {
      
      let params: GetChatConversationShareModel = {
        robotId: this.robotId,
        type: 'ai',
        client: 0,
        share: Number(this.isShare)
      }
      
      if (this.isShare) {
        params = {
          ...params,
          loginUserId: this.getUserId(),
          tenantId: this.tenantId
        }
      }
      
      return params
    },
    /** 
     * @description 是否禁用
    */
    disabled(): boolean {
      return (
        this.fetchSendNewMessageLoading
        || this.fetchHistoryMessageLoading
        || this.fetchContentSecurityTextLoading
        || this.loading
        || this.fetchConversationLoading
        || this.chatLoading
        || this.isDisabled
        || this.isInitFail
      )
    },
    /** 
     * @description 历史消息参数
    */
    historyMessageParams(): GetChatHistoryMessageModel {
      return {
        conversationId: this.conversationId,
        pageNum: this.page.pageNum,
        pageSize: 20
      }
    },
    /** 
     * @description 是否有下一页
    */
    isHaveNextPage(): boolean {
      
      if (isFalsy(this.isFirstClickHistoryMessageTip)) {
        return Boolean(this.messagePage?.hasNextPage)
      }
      
      return Boolean(this.messagePage?.list.length)
      
    },
    /** 
     * @description 是否显示历史消息折叠
    */
    isShowHistoryFoldMessage(): boolean {
      return (
        isFalsy(this.isHaveNextPage)
        && Boolean(this.messagePage?.list.length)
      )
    },
    /** 
     * @description 是否是最后一条消息是用户消息
    */
    isLastMessageIsUserMessage(): boolean {
      return this.lastMessage?.role == OpenAIMessageRoleEnum.User
    },
    /** 
     * @description 最后一条消息索引
    */
    lastMessageIndex(): number {
      return this.messages.length - 1
    },
    /** 
     * @description 最后一条消息
    */
    lastMessage(): OpenAIMessageItemType | undefined {
      return this.messages[this.lastMessageIndex]
    },
    /** 
     * @description 样式类名
    */
    viewClassNames(): Record<string, boolean> {
      return {
        [ComponentNameEnum.ChatAIView]: true,
        [`${ComponentNameEnum.ChatAIView}--focus`]: this.focusNumber > 0
      }
    },
    promptWordList(): SettingGPTPromptWordItem[] {
      return this.currentApp?.setting?.promptWordList || []
    },
    robotAppId(): string {
      return String(this.currentApp?.id || '')
    },
    robotAppType(): string {
      return String(this.currentApp?.type || '')
    },
    isBiType(): boolean {
      return this.robotAppType == SettingGPTServiceTypeEnum.BI
    },
    isIntelligentType(): boolean {
      return this.robotAppType == SettingGPTServiceTypeEnum.Intelligent
    },
    appTagClass(): Record<string, boolean> {
      return {
        'chat-ai-view-footer-app-tag': true,
        'chat-ai-view-footer-app-tag--disabled': this.disabled
      }
    }
  },
  watch: {
    loading: {
      immediate: true,
      handler(loading) {
        if (loading) {
          this.loadingInstance = Loading.service({
            target: this.$el as HTMLElement,
            fullscreen: true,
            lock: true,
            text: '正在启用您的智能 AI 助手...'
          })
        } else {
          this.loadingInstance?.close()
        }
      }
    }
  },
  mounted() {
    this.initializeFetch()
  },
  methods: {
    activeHandler() {
      this.$nextTick(() => {
        this.scrollMessageListToBottom()
      })
    },
    /** 
     * @description 添加用户消息
    */
    addUserMessage(content: string, isSendToServer: boolean) {
      // 添加一条用户消息
      const userMessage = getUserMessageByContent(content)
      // 是否推送到服务器
      const _isSendToServer = isSendToServer
      // 推送消息
      return this.pushMessage(userMessage, _isSendToServer)
    },
    /** 
     * @description 添加助理加载消息
    */
    addAILoadingMessage() {
      
      // 如果最后一条消息不是用户消息，则不添加助理加载消息
      if (isFalsy(this.isLastMessageIsUserMessage)) {
        return
      }
      
      // 添加一条助理加载消息
      const assistantLoadingMessage = getAssistantLoadingMessage()
      // 不推送到服务器
      const isSendToServer = false
      // 推送消息
      return this.pushMessage(assistantLoadingMessage, isSendToServer)
      
    },
    /** 
     * @description 添加助理停止消息
    */
    addAIStopMessage() {
      
      // 如果最后一条消息不是用户消息，则不添加助理加载消息
      if (isFalsy(this.isLastMessageIsUserMessage)) {
        return
      }
      
      // 添加一条助理停止
      const assistantStopMessage = getAssistantStopMessage()
      // 不推送到服务器
      const isSendToServer = false
      // 推送消息
      return this.pushMessage(assistantStopMessage, isSendToServer)
      
    },
    /** 
     * @description 清空消息
    */
    clearMessages() {
      this.messages = []
    },
    /** 
     * @description 清空输入框消息
    */
    clearChatAIInputBarMessageContent() {
      this.chatAIInputBarComponent?.clearMessageContent()
    },
    /** 
     * @description 删除消息
    */
    deleteMessage(index: number) {
      this.messages.splice(index, 1)
    },
    /** 
     * @description 删除助理加载消息
    */
    deleteAILoadingMessage() {
      
      // 获取最后一条消息
      const lastMessage = this.lastMessage
      // 获取默认的助理加载消息
      const assistantLoadingMessage = getAssistantLoadingMessage()
      // 判断最后一条消息是否是助理加载消息
      const isLastMessageIsAssistantLoadingMessage = (
        lastMessage?.role == OpenAIMessageRoleEnum.Assistant 
        && lastMessage?.content == assistantLoadingMessage.content
      )
      
      // 删除助理加载消息
      const lastMessageIndex = this.messages.length - 1
      isLastMessageIsAssistantLoadingMessage && this.deleteMessage(lastMessageIndex)
    },
    /** 
     * @description 内容安全文本检测
    */
    fetchContentSecurity(messageContent: string) {
      
      const params = {
        content: messageContent
      }
      
      return (
        this.fetchContentSecurityText(params).then(result => {
          
          return result
          
        }).catch(error => {
          
          return false
          
        })
      )
      
    },
    getUserId() {
      
      // 非分享，获取当前租户的用户 id
      if (isFalsy(this.isShare)) {
        const rootWindowInitData = getRootWindowInitData()
        const userId = rootWindowInitData?.user?.userId || ''
        return userId
      }
      
      return this.userId
    },
    /**
     * @description 获取聊天基础参数
     */
    getChatBaseParams(): ChatBaseParams {
      return {
        tenantId: this.tenantId,
        userId: this.getUserId(),
        conversationId: uuid(),
        type: ChatSourceDocumentTypeEnum.Wiki,
        robotId: this.robotId,
        vectorStoreType: "azure_search",
        stream: this.stream,
        isAllowUseCommonModel: true,
        appId: this.currentApp?.id as string
      }
      
    },
    /** 
     * @description 初始化
    */
    initialize() {
      this.clearMessages()
      this.initSystemMessage()
    },
    async initializeFetchBase(initAfterCallback: VoidFunction) {
      try {
        
        this.isFirstClickHistoryMessageTip = true
        this.page.pageNum = 1
        this.messageList = []
        
        this.showLoading()
        
        await this.fetchChatConversation(this.conversationParams)
        
        // 初始化聊天
        await this.initChat()
        
        // 初始化后异步回调
        if (isAsyncFunction(initAfterCallback)) {
          await initAfterCallback()
        }
        
        // 初始化后同步回调
        if (isFunction(initAfterCallback)) {
          initAfterCallback()
        }
        
        const IMMessages = this.messageList
        
        // 如果没有历史消息，则初始化一条系统消息
        if (isEmpty(IMMessages)) {
          this.initialize()
          return
        }
        
        this.$nextTick(() => {
          this.scrollMessageListToBottom()
        })
        
      } catch (error) {
        console.error(error)
      } finally {
        this.hideLoading()
      }
    },
    /** 
     * @description 初始化请求
    */
    async initializeFetch() {
      this.initializeFetchBase(this.initAfterBaseCallback)
    },
    async initializeFetchWithChangeApp() {
      
      const initAfterCallback = () => {
        this.initAfterChangeAppCallback()?.then(() => {
          this.addHistoryLastMessage()
        })
      }
      
      await this.initializeFetchBase(initAfterCallback)
      
    },
    initAfterBaseCallback() {
      
      // 获取历史消息 - 如果是分享，则不获取历史消息
      if (this.isShare) {
        return
      }
      
      return this.fetchHistoryMessage(this.historyMessageParams)
      
    },
    initAfterChangeAppCallback() {
      return this.initAfterBaseCallback()
    },
    addHistoryLastMessage() {
      
      // 如果没有历史消息，则不添加
      if (isFalsy(this.isHaveNextPage)) {
        return
      }
      
      // 原始消息列表
      const originMessageList = this.messagePage?.list || []
      // 消息列表，需要反转下数据
      const messages = originMessageList.map(chatIMMessageToAIMessage).reverse()
      
      // 最后一条消息
      const lastMessage = messages[messages.length - 1]
      // 倒数第二条消息
      const secondLastMessage = messages[messages.length - 2]
      
      // 最后一条消息是用户消息
      const isLastMessageIsUserMessage = isUserMessage(lastMessage)
      // 最后一条消息是助理消息
      const isLastMessageIsAssistantMessage = isAssistantMessage(lastMessage)
      // 倒数第二条消息是用户消息
      const isSecondLastMessageIsUserMessage = isUserMessage(secondLastMessage)
      
      // 如果最后一条消息是用户消息, 则只添加这条消息，并且删除消息列表中的最后一条消息
      if (isLastMessageIsUserMessage) {
        this.unshiftMessage(lastMessage)
        this.deleteMessagePageListHandler(1)
        return
      }
      
      // 如果最后一条消息是助理消息, 且倒数第二条消息是用户消息, 则只添加这两条消息, 并且删除消息列表中的最后两条消息
      if (isLastMessageIsAssistantMessage && isSecondLastMessageIsUserMessage) {
        
        this.unshiftMessage(lastMessage)
        this.unshiftMessage(secondLastMessage)
        
        this.deleteMessagePageListHandler(2)
        
        return
      }
      
    },
    deleteMessagePageListHandler(deleteNumber: number) {
      const newMessageList = this.messagePage.list.reverse().slice(0, -deleteNumber).reverse()
      this.updateMessagePageHandler(newMessageList)
    },
    updateMessagePageHandler(newMessageList: ChatIMMessageItemType[]) {
      
      const newMessagePage = new Page({
        ...this.messagePage,
        list: newMessageList
      })
      
      this.setMessagePage(newMessagePage)
      
    },
    /** 
     * @description 进入页面时，默认初始化一条系统消息，用于显示
    */
    initSystemMessage() {
      const systemMessage = getSystemMessageByContent(
        this.chatSystemMessage
        || DEFAULT_CHAT_SYSTEM_MESSAGE
      )
      const isSendToServer = false
      this.pushMessage(systemMessage, isSendToServer)
    },
    /** 
     * @description 初始化聊天
    */
    async initChat() {
      
      if (this.isBiType) {
        return
      }
      
      const params: ChatInitParams = this.getChatBaseParams()
      
      const result = await initChat(params)
      
      // 如果初始化失败，则提示错误消息
      if (isFalsy(result?.success)) {
        this.isInitFail = true
        this.$message.error(result?.message)
        return
      }
      
      const id = result?.data
      this.initId(id)
      
    },
    /** 
     * @description 初始化 AI 应用聊天 id
    */
    initId(id: string | undefined | null) {
      this.id = id || ''
    },
    unshiftMessage(message: OpenAIMessageItemType) {
      
      if (isUndefined(message.likeStatus)) {
        message.likeStatus = undefined
      }
      
      this.messages.unshift(message)
      
      this.$nextTick(() => {
        this.scrollMessageListToBottom()
      })
      
      setTimeout(() => {
        this.scrollMessageListToBottom()
      }, 300)
      
    },
    sleep(duration: number) {
      return new Promise((resolve, reject) => {
        setTimeout(resolve, duration);
      })
    },
    /** 
     * @description 添加消息
    */
    async pushMessage(message: OpenAIMessageItemType, isSendToServer: boolean = true, delay: number = 0) {
      
      if (isUndefined(message.likeStatus)) {
        message.likeStatus = undefined
      }
      
      this.messages.push(message)
      
      this.$nextTick(() => {
        this.scrollMessageListToBottom()
      })
      
      setTimeout(() => {
        this.scrollMessageListToBottom()
      }, 300)
      
      if (isFalsy(isSendToServer)) {
        return
      }
      
      // 小宝BI暂时不往服务器推送消息
      if (this.isBiType) {
        return
      }
      
      if (isUserMessage(message)) {
        
        if (delay) {
          await this.sleep(delay)
        }
        
        return this.sendNewMessageByUser(message)
      }
      
      if (isNotUserMessage(message)) {
        
        if (delay) {
          await this.sleep(delay)
        }
        
        return this.sendNewMessageByBot(message)
      }
      
    },
    /** 
     * @description 设置消息列表
    */
    setMessages(messages: OpenAIMessageItemType[]) {
      this.messages = messages
    },
    sendNewMessageByUser(message: OpenAIMessageItemType) {
      
      let params: SendChatConversationContentShareModel = {
        sender: this.getUserId(),
        content: message.content || '',
        contentType: "text",
        type: "user",
        conversationId: this.conversationId,
        share: Number(this.isShare)
      }
      
      if (this.isShare) {
        params = {
          ...params,
          tenantId: this.tenantId,
          loginUserId: this.getUserId()
        }
      }
      
      return this.fetchSendNewMessage(params).then(conversationContentId => {
        message.id = conversationContentId
      })
      
    },
    sendNewMessageByBot(message: OpenAIMessageItemType) {
      
      let params: SendChatConversationContentShareModel = {
        sender: this.robotId,
        content: message.content || '',
        contentType: "text",
        type: "ai",
        conversationId: this.conversationId,
        share: Number(this.isShare)
      }
      
      if (this.isShare) {
        params = {
          ...params,
          tenantId: this.tenantId,
          loginUserId: this.getUserId()
        }
      }
      
      return this.fetchSendNewMessage(params).then(conversationContentId => {
        message.id = conversationContentId
      })
      
    },
    /** 
     * @description 历史消息点击事件
    */
    onHistoryMessageTipClickHandler() {
      
      if (this.disabled) {
        return
      }
      
      if (this.foldHistoryMessageList.length) {
        
        const messages = [
          ...this.foldHistoryMessageList,
          ...this.messages
        ]
        
        this.setMessages(messages)
        this.foldHistoryMessageList = []
        this.messagePage.hasNextPage = false
        
        return
      }
      
      if (
        this.isFirstClickHistoryMessageTip 
        && this.page.pageNum == 1 
        && isNotEmpty(this.messagePage?.list)
      ) {
        
        // 原始的后端消息列表，需要反转下数据
        const originServerMessages = this.messagePage.list.map(chatIMMessageToAIMessage).reverse()
        
        // 当前的消息列表 Map 对象
        const currentMessagesMap = new Map()
        this.messages.forEach(message => {
          currentMessagesMap.set(message.id, message)
        })
        
        // 消息列表根据 id 去重
        const removeDuplicatesMessages = originServerMessages.filter(message => {
          return isFalsy(currentMessagesMap.has(message.id))
        })
        
        // 设置消息列表
        this.setMessages([...removeDuplicatesMessages, ...this.messages])
        
        // 更新状态 - 不是第一次点击历史消息
        this.isFirstClickHistoryMessageTip = false
        
        return
      }
      
      this.page.pageNum++
      
      this.fetchHistoryMessage(this.historyMessageParams).then((messageList: ChatIMMessageItemType[]) => {
        // 消息列表，需要反转下数据
        const messages = messageList.map(chatIMMessageToAIMessage).reverse()
        this.setMessages([...messages, ...this.messages])
      })
    },
    /** 
     * @description 历史消息折叠点击事件
    */
    onHistoryMessageTipFoldClickHandler() {
      
      const historyMessageList = this.messages.filter(message => message?.isHistory)
      this.foldHistoryMessageList = cloneDeep(historyMessageList)
      
      const isNotShowHistoryMessageList = this.messages.filter(message => isFalsy(message?.isHistory))
      
      this.setMessages(isNotShowHistoryMessageList)
      
      this.messagePage.hasNextPage = true
      
    },
    /** 
     * @description 输入框聚焦事件
    */
    onInputBarFocusHandler() {
      this.focusNumber++
    },
    async onStopHandler() {
      try {
        
        if (this.isBiType) {
          await textToReports({} as any)
        } else if (this.stream) {
          this.controller?.abort()
        } else {
          await conversationChat({} as ChatConversationParams)
        }
        
        this.deleteAILoadingMessage()
        this.addAIStopMessage()
        
      } catch (error) {
        console.error(error)
      }
    },
    /** 
     * @description 重新回答 事件
    */
    onRefreshHandler() {
      
      // 当前的消息列表
      const messages = this.messages
      
      // 最后一条用户消息内容
      let lastUserMessageContent = ''
      // 最后第二条消息索引
      let lastSecondMessageIndex = messages.length - 2
      
      // 倒数第二条消息是用户消息，则取这条消息的内容
      const lastSecondMessage = messages[lastSecondMessageIndex]
      const isLastSecondMessageIsUserMessage = isUserMessage(lastSecondMessage)
      
      if (isFalsy(isLastSecondMessageIsUserMessage)) {
        throw new Error('倒数第二条消息不是用户消息')
      }
      
      const lastMessage = messages[messages.length - 1]
      const lastMessageId = lastMessage?.id
      
      // 删除当前 AI 回复的消息
      this.fetchDeleteMessage({
        conversationId: this.conversationId,
        conversationContentId: lastMessageId
      })
      
      lastUserMessageContent = lastSecondMessage?.content || ''
      
      // 然后清空 最后的两条消息 (一条是用户消息 + 一条是 AI 之前的回复消息)
      this.setMessages(
        messages.slice(0, lastSecondMessageIndex)
      )
      
      // 当前这次重新回答的用户消息，不推送到服务器
      const isUserMessageSendToServer = false
      // 是否清空输入框内容
      const isClearInputContent = false
      // 发送消息
      this.onSendButtonClickHandler(lastUserMessageContent, isUserMessageSendToServer, isClearInputContent)
      
    },
    /** 
     * @description 发送消息按钮点击事件
    */
    async onSendButtonClickHandler(messageContent: string, isSendToServer: boolean = true, isClearInputContent: boolean = true) {
      try {
        
        // 正在加载中，则不发送消息
        if (this.loading) {
          return
        }
        
        this.chatLoading = true
        
        const content = messageContent.trim()
        // 消息内容为空，则不发送消息
        if (isFalsy(content)) {
          this.$message.error('请输入问题')
          // 清空输入框消息
          isClearInputContent && this.clearChatAIInputBarMessageContent()
          return
        }
        
        // 内容安全检测
        await this.validateContentSecurity(content)
        
        // 添加用户消息
        await this.addUserMessage(content, isSendToServer)
        
        // 默认添加一条助理加载消息
        setTimeout(() => {
          this.addAILoadingMessage()        
        })
        
        // 清空输入框消息
        isClearInputContent && this.clearChatAIInputBarMessageContent()
        
        // 发送消息
        const result = await this.sendMessage(content)
        // 删除 AI 助理加载消息
        this.deleteAILoadingMessage()
        
        this.$nextTick(() => {
          this.scrollMessageListToBottom()
        })
        
        // 添加助理回复消息
        if (result) {
          const isSendToServer = true
          const delay = 1000
          await this.pushMessage(result, isSendToServer, delay)
        }
        
        this.$nextTick(() => {
          this.scrollMessageListToBottom()
        })
        
        const times = [0, 300, 500, 1000]
        times.forEach(time => {
          setTimeout(() => {
            this.scrollMessageListToBottom()
          }, time)
        })
        
      } finally {
        this.chatLoading = false
        this.isStreaming = false
        this.hideLoading()
      }
    },
    /** 
     * @description 发送消息会话问题 到 服务端
    */
    async sendMessage(content: string) {
      
      // if (this.isIntelligentType) {
      //   // 验证问题是否在标准库中
      //   const standardQuestionResult = await this.fetchQuestionInStandardLibImpl(content)
      //   if (standardQuestionResult) {
      //     // 标准库中的问题答案
      //     const answer = standardQuestionResult?.answer || ''
      //     const message: OpenAIMessageItemType = {
      //       // 消息内容
      //       content: answer,
      //       // 消息类型
      //       role: OpenAIMessageRoleEnum.Assistant,
      //       // 业务数据来源列表
      //       businessSourceDocuments: [],
      //       id: uuid()
      //     }
          
      //     return message
      //   }
      // }
      
      if (this.isBiType) {
        
        const result = await textToReports({ text: content })
        
        if (MsgModel.isFail(result)) {
          const message: OpenAIMessageItemType = {
            role: OpenAIMessageRoleEnum.Assistant,
            id: uuid(),
            isBI: true,
            content: result.message
          }
          return message
        }
        
        const message: OpenAIMessageItemType = {
          role: OpenAIMessageRoleEnum.Assistant,
          id: uuid(),
          isBI: true,
          echartData: result.data,
        }
        
        return message
      }
      
      const loadingMessage = getAssistantLoadingMessage()
      
      // 过滤掉空消息
      let validMessages = this.messages.filter(message => Boolean(message?.content))
      // 过滤掉助理加载消息
      validMessages = validMessages.filter(message => {
        return isNotEqual(message?.content, loadingMessage.content)
      })
      // 过滤掉历史消息
      validMessages = validMessages.filter(message => isFalsy(message?.isHistory))
      // 过滤掉已停止的消息
      validMessages = validMessages.filter(message => isFalsy(message?.isStop))
      
      // 过滤掉 AI 回复的超长消息
      validMessages = validMessages.filter(message => {
        return Number(message?.content?.length || 0) <= 1000
      })
      
      // 如果上次助理消息是错误消息，则只取最后一条消息，没错则取最后20条消息
      const messageNumber = this.isLastAssistantMessageError ? 1 : 20
      const contextMessages = validMessages.slice(-messageNumber)
      
      const newContextMessages = contextMessages.map(message => {
        return {
          content: message.content,
          type: message.role
        }
      })
      
      const baseParams: ChatBaseParams = this.getChatBaseParams()
      const params: ChatConversationParams = {
        ...baseParams,
        conversationId: this.id || uuid(),
        messages: newContextMessages,
      }
      
      // 如果流状态为 true，则使用流接收消息
      if (this.stream) {
        return this.sendMessageWithStream(params)
      }
      
      try {
        
        const result = await conversationChat(params)
        this.isLastAssistantMessageError = isFalsy(result?.success)
        
        if (isFalsy(result?.success)) {
          this.$message.error(result?.message)
          return
        }
        
        const data = result?.data || {} as ChatConversationResult
        // AI 回复的消息内容
        let resultContent = data?.content || ''
        // AI 查询的业务数据来源列表
        const businessSourceDocuments: ChatBusinessSourceDocumentType[] = data?.businessSourceDocuments || []
        
        if (isString(data)) {
          resultContent = data as unknown as string
        }
        
        const message: OpenAIMessageItemType = {
          // 消息内容
          content: resultContent,
          // 消息类型
          role: OpenAIMessageRoleEnum.Assistant,
          // 业务数据来源列表
          businessSourceDocuments: businessSourceDocuments,
          id: uuid()
        }
        
        return message
        
      } catch (error) {
        
        this.isLastAssistantMessageError = true
        
        console.error(error)
        
        return
        
      }
      
    },
    /** 
     * @description 发送消息会话问题 (流式)
    */
    async sendMessageWithStream(params: Record<string, any>) {
      
      const controller = new AbortController();
      
      this.controller = controller
      this.isStreaming = true
      
      const response = await fetch('/language/chat/conversation', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        signal: controller.signal,
        body: JSON.stringify(params),
      });
      
      if (!response.ok) {
        return;
      }
      
      const data = response.body;
      
      if (!data) {
        return;
      }
      
      const reader = data.getReader();
      const decoder = new TextDecoder();
      let done = false;
      let text = '';
      let businessSourceDocumentsJSON = '';
      
      while (!done) {
        
        const { value, done: doneReading } = await reader.read();
        
        done = doneReading;
        
        let chunkValue = decoder.decode(value);
        
        const dataPrefix = 'data:';
        chunkValue = chunkValue.startsWith(dataPrefix) ? chunkValue.slice(dataPrefix.length) : chunkValue;
        chunkValue = chunkValue.trim();
        
        try {
          chunkValue = chunkValue.replaceAll(dataPrefix, '');
        } catch (error) {
          chunkValue = chunkValue.replace(new RegExp(dataPrefix, 'g'), '');
        }
        
        // 如果是业务数据来源，则单独处理
        if (
          chunkValue.startsWith(STREAM_BUSINESS_SOURCE_DOCUMENTS)
        ) {
          businessSourceDocumentsJSON = chunkValue.replace(STREAM_BUSINESS_SOURCE_DOCUMENTS, '')
        }   
        // 如果是业务数据来源，单独处理
        else if (chunkValue.includes(STREAM_BUSINESS_SOURCE_DOCUMENTS)) {
          try {
            const index = chunkValue.indexOf(STREAM_BUSINESS_SOURCE_DOCUMENTS)
            text += chunkValue.slice(0, index)
            businessSourceDocumentsJSON = chunkValue.slice(index + STREAM_BUSINESS_SOURCE_DOCUMENTS.length)
          } catch (error) {
            console.error(error)
          }
        } 
        else {
          text += chunkValue;
        }
        
        // 更新助理消息
        this.messages[this.messages.length - 1].content = text
        
        // 滚动消息列表到底部
        this.scrollMessageListToBottom();
        
      }
      
      this.isLastAssistantMessageError = isFalsy(text);
      
      if (isFalsy(text)) {
        
        this.$message.error('网络异常，请稍后再试')
        
        if (isFalsy(this.lastMessage?.content)) {
          this.messages.pop()
        }
        
        return
      }
      
      const businessSourceDocuments = parse_with_default_value(businessSourceDocumentsJSON, [])
      const message: OpenAIMessageItemType = {
        // 消息内容
        content: text,
        // 消息类型
        role: OpenAIMessageRoleEnum.Assistant,
        // 业务数据来源列表
        businessSourceDocuments: businessSourceDocuments,
        id: uuid()
      }
      
      const lastMessageIndex = this.messages.length - 1
      this.deleteMessage(lastMessageIndex)
      
      return message
    },
    /** 
     * @description 滚动消息列表到底部
    */
    scrollMessageListToBottom() {
      
      if (this.chatAIMessageListElement) {
        this.chatAIMessageListElement.scrollTop = 100000
      }
      
    },
    /** 
     * @description 设置输入框消息内容
    */
    setInputMessageContent(messageContent: string) {
      this.chatAIInputBarComponent?.onInputHandler(messageContent)
    },
    /** 
     * @description 内容安全文本检测
     * @param {string} content 消息内容
     * 
    */
    async validateContentSecurity(content: string) {
      
      const isPaasContentSecurity = await this.fetchContentSecurity(content)
      
      // 如果不通过，则不发送消息, 并将当前输入框的值设置为原始消息内容
      if (isFalsy(isPaasContentSecurity)) {
        
        this.setInputMessageContent(content)
        this.hideLoading()
        
        const errorMessage = '消息内容含有敏感词，请重新输入'
        
        if (isFalsy(isPaasContentSecurity)) {
          Platform.alert(errorMessage)
        }
        
        throw new Error(errorMessage)
        
      }
      
    },
    fetchQuestionInStandardLibImpl(content: string) {
      
      const params = {
        question: content,
        tenantId: this.tenantId
      }
      
      try {
        return this.fetchQuestionInStandardLib(params)
      } catch (error) {
        return Promise.resolve(null)
      }
      
    },
    onClickAppTagHandler() {
      
      if (this.disabled) {
        return
      }
      
      this.$emit(ChatAIViewEventEnum.AppTag)
    },
    onPromptWordClickHandler(value: string) {
      this.setInputMessageContent(value)
    }
  },
  render() {
    return (
      <div class={this.viewClassNames}>
        
        {/* start 消息列表 */}
        <div 
          class="chat-ai-view-message-list"
          ref="ChatAIMessageListElement"
        >
          
          {/* start 显示历史消息按钮 */}
          {this.isHaveNextPage && (
            <chat-ai-history-message-tip
              onClick={this.onHistoryMessageTipClickHandler}
            >
            </chat-ai-history-message-tip>
          )}
          {/* end 显示历史消息按钮 */}
          
          {/* start 显示折叠历史消息按钮 */}
          {this.isShowHistoryFoldMessage && (
            <chat-ai-history-message-tip
              isDone
              onClick={this.onHistoryMessageTipFoldClickHandler}
            >
            </chat-ai-history-message-tip>
          )}
          {/* end 显示折叠历史消息按钮 */}
          
          <chat-ai-message-list 
            isStreaming={this.isStreaming}
            isShare={this.isShare}
            value={this.messages}
            robotId={this.robotId}
            robotAppId={this.robotAppId}
            userId={this.userId}
            tenantId={this.tenantId}
            onRefresh={this.onRefreshHandler}
            onStop={this.onStopHandler}
          >
          </chat-ai-message-list>
          
        </div>
        {/* end 消息列表 */}
        
        {/* start 底部 */}
        <div class="chat-ai-view-footer">
          
          <div class={this.appTagClass}>
            <ChatAIAppTag
              value={this.currentApp?.name}
              onClick={this.onClickAppTagHandler}
            >
            </ChatAIAppTag>
          </div>
          
          <div class="chat-ai-view-footer-prompt-word-list">
            {this.promptWordList.map((item, index) => {
              return (
                <ChatAIPromptTag
                  key={index}
                  label={item.title}
                  value={item.content}
                  onClick={this.onPromptWordClickHandler}
                >
                </ChatAIPromptTag>
              )
            })}
          </div>
          
          <chat-ai-input-bar
            ref="ChatAIInputBarComponent"
            disabled={this.disabled}
            onSend={this.onSendButtonClickHandler}
            onFocus={this.onInputBarFocusHandler}
          >
          </chat-ai-input-bar>
        </div>
        {/* end 底部 */}
        
      </div>
    ) as any
  }
})
